/*
 * Filename: server_tcp_epoll.c
 * Description: TODO: Add description
 *
 * Created: 2025-05-29
 * Author: lyh
 */

#include <my_header.h>

#define MAXSIZE_FD 1024
#define TIMEOUT_LIMIT 10
// #define CAPICITY_ROOM 2

typedef struct fd_events_data {
	int all_fd[MAXSIZE_FD];
	struct epoll_event reay_fd_events[MAXSIZE_FD];
	time_t last_alive_time[MAXSIZE_FD];
	int size_of_alive;
	int capicity_room;
	bool server_fd_isWoring;
} FED;

void quit(int *fd, int *epfd);

int main(int argc, char *argv[]) {
	// ./server_tcp_epoll 192.168.48.128 12345
	ARGS_CHECK(argc, 3);
	// 准备地址
	struct sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr(argv[1]);
	server_addr.sin_port = htons(atoi(argv[2]));
	// 初始socket,绑定服务端端口
	int server_fd = socket(AF_INET, SOCK_STREAM, 0);
	ERROR_CHECK(server_fd, -1, "socket error");
	int tag = bind(server_fd, (struct sockaddr *)&server_addr,
				   sizeof(struct sockaddr_in));
	ERROR_CHECK(tag, -1, "bind error");
	listen(server_fd, 10);

	// 设置epoll监听集合 fd epoll_event;
	int epfd = epoll_create(1);
	struct epoll_event add_fd_events, ready_fd_events;
	add_fd_events.data.fd = server_fd;
	add_fd_events.events = EPOLLIN;
	tag = epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &add_fd_events);
	ERROR_CHECK(tag, -1, "epoll_ctl error");

	char buffer[4096];
	FED dataset = {0};
	dataset.server_fd_isWoring = true;
	dataset.capicity_room = 2;
	while (1) {
		ready_fd_events.data.fd = -1;
		int maxcount = epoll_wait(epfd, &ready_fd_events, 1, 1000);
		ERROR_CHECK(maxcount, -1, "epoll_wat error");
		// ERROR_CHECK(maxcount, 0, "epoll_wait interrupt");
		if (ready_fd_events.data.fd == server_fd) {
			if (dataset.size_of_alive >= dataset.capicity_room) {
				epoll_ctl(epfd, EPOLL_CTL_DEL, server_fd, NULL);
				dataset.server_fd_isWoring = false;
				continue;
			}
			int clientfd = accept(server_fd, NULL, NULL);
			add_fd_events.data.fd = clientfd;
			epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &add_fd_events);
			dataset.all_fd[clientfd] = 1;
			dataset.last_alive_time[clientfd] = time(NULL);
			dataset.size_of_alive++;
			printf("%d join in\n", clientfd);
		} else {
			if (maxcount) {
				bzero(buffer, 4096);
				sprintf(buffer, "clinetid %d :", ready_fd_events.data.fd);
				int name_len = strlen(buffer);
				int recvcout =
					recv(ready_fd_events.data.fd, buffer + name_len, 4096, 0);
				if (recvcout == 0) {
					dataset.all_fd[ready_fd_events.data.fd] = 0;
					dataset.size_of_alive--;
					if (!dataset.server_fd_isWoring) {
						add_fd_events.data.fd = server_fd;
						epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd,
								  &add_fd_events);
					}
					printf("%d quit\n", ready_fd_events.data.fd);
					quit(&ready_fd_events.data.fd, &epfd);
				} else {
					dataset.last_alive_time[ready_fd_events.data.fd] =
						time(NULL);
					// broad others
					dataset.all_fd[ready_fd_events.data.fd] = 0;
					for (int i = 0; i < MAXSIZE_FD;
						 i++) // 可以使用链表优化,过滤无效数据
					{
						if (dataset.all_fd[i]) {
							int sendttag =
								send(i, buffer, recvcout + name_len, 0);
							if (sendttag <= 0) {
								dataset.all_fd[i] = 0;
								dataset.size_of_alive--;
								if (!dataset.server_fd_isWoring) {
									add_fd_events.data.fd = server_fd;
									epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd,
											  &add_fd_events);
								}
								quit(&i, &epfd);
								perror("send error");
							}
						}
					}
					dataset.all_fd[ready_fd_events.data.fd] = 1;
				}
			}
			// 清理超时clients
			time_t now = time(NULL);
            printf("current time %s\n",ctime(&now));
			for (int i = 0; i < MAXSIZE_FD; i++) {
				if (dataset.all_fd[i] &&
					now - dataset.last_alive_time[i] > TIMEOUT_LIMIT) {
					if (!dataset.server_fd_isWoring) {
						add_fd_events.data.fd = server_fd;
						epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd,
								  &add_fd_events);
					}
					dataset.all_fd[i] = 0;
					dataset.size_of_alive--;
					int id=i;
                    quit(&id, &epfd);
					printf("clientfd %d timeover\n", i);
				}
			}
		}
	}
	close(server_fd);
	close(epfd);
	return 0;
}

void quit(int *fd, int *epfd) {
	if (*fd != -1) {
		epoll_ctl(*epfd, EPOLL_CTL_DEL, *fd, NULL);
		close(*fd);
		*fd = -1;
	}
}
